/*
 * jngc, Java utility classes by Nicholas Coltharp
 * Copyright (C) 2010 Nicholas Coltharp <ngc1@rice.edu>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package edu.rice.ngc1.util;

/**
 * A {@code Counter} with a maximum value.
 * 
 * Whenever a {@code WrappingCounter} is incremented and would hit its
 * maximum value, it resets to 0.
 */
public class WrappingCounter extends Counter {

    /**
     * Serial version UID.
     */
    private static final long serialVersionUID = -6334172399992458807L;

    /**
     * This object's maximum value.
     */
    private final int max;

    /**
     * Constructs a new {@code WrappingCounter} with the given maximum
     * value and current value.
     * 
     * @param max
     *            the new counter's max value; must be greater than 0
     * @param value
     *            value to start with; must be at least 0 and less
     *            than {@code max}
     */
    public WrappingCounter(int max, int value) {
        super(value);
        if (max <= 0) {
            throw new IllegalArgumentException("max must be greater than 0; "
                    + "got " + max);
        }
        if (value >= max) {
            throw new IllegalArgumentException("value must be less than max;"
                    + "got max: " + max + ", value: " + value);
        }
        this.max = max;
    }

    /**
     * Constructs a new {@code WrappingCounter} with the given maximum
     * value and a value of 0.
     * 
     * @param max
     *            the new counter's max value
     */
    public WrappingCounter(int max) {
        this(max, 0);
    }

    /**
     * Constructs a new {@code WrappingCounter} with the same value
     * and maximum as the given one.
     * 
     * @param orig
     *            the object to copy from
     */
    public WrappingCounter(WrappingCounter orig) {
        super(orig);
        this.max = orig.max;
    }

    /**
     * Returns this counter's maximum value.
     * 
     * @return this counter's maximum value
     */
    public int getMax() {
        return this.max;
    }

    /**
     * Gets the maximum value that this counter's value may equal
     * without resetting.
     * 
     * @return {@link #getMax()} - 1
     */
    private int getCeiling() {
        return this.getMax() - 1;
    }

    /**
     * If this counter's value is one less than its maximum, then this
     * resets the counter. Otherwise, it increments it as normal.
     */
    @Override
    public int increment() {
        if (this.getValue() == this.getCeiling()) {
            return this.reset();
        } else {
            return super.increment();
        }
    }

    /**
     * Clones this object.
     * 
     * @return a new {@code WrappingCounter} whose count and max count
     *         are the same as this one's
     */
    @Override
    public WrappingCounter clone() {
        return new WrappingCounter(this);
    }

    /**
     * Checks this object for equality with another one, obeying the
     * general contract of {@code Object#hashCode()}.
     * 
     * @param obj
     *            the object to check against
     * @return {@code true} if {@code obj} has the same count and max
     *         count as this object; {@code false} otherwise
     */
    @Override
    public boolean equals(Object obj) {
        if (!super.equals(obj)) {
            return false;
        }
        WrappingCounter other = (WrappingCounter) obj;
        return (other.max == this.max);
    }

    /**
     * Obeys the general contract of {@link Object#hashCode()}.
     * 
     * @return this object's hash code
     */
    @Override
    public int hashCode() {
        int hash = HashCodeGenerator.getInitialValue();
        hash = HashCodeGenerator.hash(hash, super.hashCode());
        hash = HashCodeGenerator.hash(hash, max);
        return hash;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder("Counter(max=");
        builder.append(max);
        builder.append(", ");
        builder.append(getValue());
        builder.append(")");
        return builder.toString();
    }
}
